home *** CD-ROM | disk | FTP | other *** search
/ BBS in a Box 3 / BBS in a box - Trilogy III.iso / Files / Prog / B-C / C++ FAQ Reference 1.0 / C++ FAQ Reference 1.0.rsrc / TEXT_1734.txt < prev    next >
Encoding:
Text File  |  1993-06-30  |  2.9 KB  |  53 lines

  1. The best answer is: buy a compiler that supports templates.  When this is not feasible, the next best answer is to buy or build a template preprocessor (ex: reads C++-with-templates, outputs C++-with-expanded-template-classes; such a system needn't be perfect; it cost my company about three man-weeks to develop such a preprocessor).  If neither of these is feasible, you can use the macro preprocessor to fake templates.  But beware: it's tedious; templates are a better solution wrt development and maintenance costs.
  2.  
  3. Here's how you'd declare my 'Vec' example from above.  First we define 'Vec(T)' to concatenate the name 'Vec' with that of the type T (ex: Vec(String) becomes 'VecString').  This would go into a header file such as Vec.h:
  4.  
  5.     #include <generic.h>    //to get the 'name2()' macro
  6.     #define  Vec(T)  name2(Vec,T)
  7.  
  8. Next we declare the class Vec(T) using the name 'Vecdeclare(T)' (in general you would postfix the name of the class with 'declare', such as 'Listdeclare' etc):
  9.  
  10.     #define Vecdeclare(T)                        \
  11.     class Vec(T) {                            \
  12.       int  xlen;                            \
  13.       T*   xdata;                            \
  14.       int  check(int i);  /*return i if in bounds else throw*/    \
  15.     public:                                \
  16.       int       len()             const { return xlen;     }    \
  17.       const T&  operator[](int i) const { xdata[check(i)]; }    \
  18.             T&  operator[](int i)       { xdata[check(i)]; }    \
  19.                 Vec(T)(int L=10): xlen(L), xdata(new T[L]) {/*...*/}\
  20.                ~Vec(T)()                   { delete [] xdata; }    \
  21.     };
  22.  
  23. Note how each occurrence of 'Vec' has the '(T)' postfixed.  Finally we set up another macro that 'implements' the non-inline member function(s) of Vec:
  24.  
  25.     //strangely enough this can also go into Vec.h
  26.     #define Vecimplement(T)                        \
  27.     int Vec(T)::check(int i)                    \
  28.     {                                \
  29.       if (i < 0 || i >= xlen) throw BoundsViol("Vec", i, xlen);    \
  30.       return i;                            \
  31.     }
  32.  
  33. When you which to use a Vec-of-String and Vec-of-int, you would say:
  34.  
  35.     #include "Vec.h"     //pulls in <generic.h> too; see below...
  36.     declare(Vec,String)  //'declare()' is a macro defined in <generic.h>
  37.     declare(Vec,int)
  38.     Vec(String) vs;      //Vec(String) becomes the single token 'VecString'
  39.     Vec(int)    vi;
  40.  
  41. In exactly one source file in the system, you must provide implementations for the non-inlined member functions:
  42.  
  43.     #include "Vec.h"
  44.     declare  (Vec,String)   declare  (Vec,int)   declare  (Vec,float)
  45.     implement(Vec,String)   implement(Vec,int)   implement(Vec,float)
  46.  
  47. Note that types whose names are other than a single identifier do not work properly.  Ex: Vec(char*) creates a class whose name is 'Vecchar*'.  The patch is to create a typedef for the appropriate type:
  48.  
  49.     #include "Vec.h"
  50.     typedef char* charP;
  51.     declare(Vec,charP)
  52.  
  53. It is important that every declaration of what amounts to 'Vec<char*>' must all use exactly the same typedef, otherwise you will end up with several equivalent classes, and you'll have unnecessary code duplication.  This is the sort of tedium which a template mechanism can handle for you.